using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Windows.Threading;
using Autofac;
using Phoenix.Diagnostics;
using Phoenix.Framework;
using Phoenix.Storage;

namespace Phoenix.Extensions.Autofac
{
    public class AutofacPhoenixHostBuilder : PhoenixHostBuilder
    {
        readonly TaskScheduler appTaskScheduler;
        readonly Dispatcher appDispatcher;
        readonly List<Action<ContainerBuilder>> containerRegistrations = new List<Action<ContainerBuilder>>();
        readonly List<Action<IContainer>> containerReadyCallbacks = new List<Action<IContainer>>();
        bool registerViewModelsWithContainer;

        public AutofacPhoenixHostBuilder(
            TaskScheduler appTaskScheduler,
            Dispatcher appDispatcher) :
            base(appTaskScheduler, appDispatcher)
        {
            this.appTaskScheduler = appTaskScheduler;
            this.appDispatcher = appDispatcher;
        }

        public void ConfigureContainer(Action<ContainerBuilder> containerRegistration)
        {
            containerRegistrations.Add(containerRegistration);
        }

        public void RegisterViewModelsWithContainer()
        {
            registerViewModelsWithContainer = true;
        }

        public Task<IPhoenixHost> Run(IPhoenixHostTarget hostTarget, IContainer existingContainerToUpdate)
        {
            var containerBuilder = new ContainerBuilder();
            var hostState = RegisterServicesWithContainer(hostTarget, containerBuilder);

            containerBuilder.Update(existingContainerToUpdate);
            return CompleteRun(hostTarget, existingContainerToUpdate, hostState);
        }

        public override Task<IPhoenixHost> Run(IPhoenixHostTarget hostTarget)
        {
            var containerBuilder = new ContainerBuilder();
            var hostState = RegisterServicesWithContainer(hostTarget, containerBuilder);
            var container = containerBuilder.Build();
            return CompleteRun(hostTarget, container, hostState);
        }

        Task<IPhoenixHost> CompleteRun(IPhoenixHostTarget hostTarget, IContainer container, TransientStore hostState)
        {
            var autofacControllerFactory = container.Resolve<IControllerFactory>() as AutofacControllerFactory;
            if (autofacControllerFactory != null)
                autofacControllerFactory.SetContainer(container);

            var autofacPhoenixHost = container.Resolve<IPhoenixHost>();

            var phoenixHost = ((AutofacPhoenixHost) autofacPhoenixHost);
            phoenixHost.Container = container;

            foreach (var containerReadyCallback in containerReadyCallbacks)
            {
                containerReadyCallback(container);
            }

            return RunBase(hostTarget, container.Resolve<IActionInvoker>(), autofacPhoenixHost, hostState)
                .ContinueWith(t =>
                    {
                        t.Wait(); //Propogate any exceptions
                        return autofacPhoenixHost;
                    });
        }

        TransientStore RegisterServicesWithContainer(IPhoenixHostTarget hostTarget, ContainerBuilder containerBuilder)
        {
            var hostState = new TransientStore();

            var controllerFactory = ControllerFactory ?? new AutofacControllerFactory();
            var controllerLocator = ControllerLocator ?? new DefaultControllerLocator();
            var viewLocator = ViewLocator ?? new DefaultViewLocator();
            var errorReporter = ErrorReporter ?? GetErrorReporter();

            containerBuilder.RegisterInstance(controllerFactory).As<IControllerFactory>();
            containerBuilder.RegisterInstance(controllerLocator).As<IControllerLocator>();
            containerBuilder.RegisterInstance(viewLocator).As<IViewLocator>();
            containerBuilder.RegisterInstance(errorReporter).As<IErrorReporter>();
            containerBuilder.RegisterType<ActionInvoker>().As<IActionInvoker>();

            foreach (var containerRegistration in containerRegistrations)
            {
                containerRegistration(containerBuilder);
            }
            foreach (var assembly in AssembliesToRegister)
            {
                assembly.RegisterControllers(containerBuilder, controllerLocator);
                if (registerViewModelsWithContainer)
                    assembly.RegisterViewModels(containerBuilder);
            }

            containerBuilder
                .RegisterType<AutofacPhoenixHost>()
                .WithParameter(new TypedParameter(typeof (IPhoenixHostTarget), hostTarget))
                .WithParameter(new TypedParameter(typeof (ITransientStore), hostState))
                .WithParameter(new TypedParameter(typeof (TaskScheduler), appTaskScheduler))
                .WithParameter(new TypedParameter(typeof (Dispatcher), appDispatcher))
                .As<IPhoenixHost>()
                .SingleInstance();

            RegisterPhoenixServices(containerBuilder);
            return hostState;
        }

        void RegisterPhoenixServices(ContainerBuilder containerBuilder)
        {
#if WINDOWS_PHONE
            containerBuilder
                .Register(c => new Tasks.PhoneTaskManager())
                .As<Tasks.ITaskManager>()
                .SingleInstance();
#endif
        }

        public void ContainerReady(Action<IContainer> callback)
        {
            containerReadyCallbacks.Add(callback);
        }
    }
}